/**
 * 
 */
package org.x.starter4j.jetty.httpclient;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.client.util.InputStreamContentProvider;
import org.eclipse.jetty.client.util.InputStreamResponseListener;
import org.eclipse.jetty.client.util.OutputStreamContentProvider;
import org.eclipse.jetty.client.util.PathContentProvider;
import org.eclipse.jetty.http.HttpMethod;

/**
 * @author huangyang@p-an.com 2013年11月11日
 */
public class DemoHttpClient
{
	public static void main(final String[] args) throws Exception
	{
		// Instantiate HttpClient
		HttpClient httpClient = new HttpClient();

		// Configure HttpClient, for example:
		httpClient.setFollowRedirects(false);

		// Start HttpClient
		httpClient.start();

		// 阻塞访问方式
		// The simpler way to perform a HTTP request
		ContentResponse response = httpClient.GET("http://domain.com/path?query");

		// customize the request, for example by issuing a HEAD request instead of a GET, and simulating a browser user agent
		response = httpClient.newRequest("http://domain.com/path?query").method(HttpMethod.HEAD).agent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0").send();

		// Simple POST requests
		response = httpClient.POST("http://domain.com/entity/1").param("p", "value").send();

		// File uploads
		response = httpClient.newRequest("http://domain.com/upload").file(Paths.get("file_to_upload.txt"), "text/plain").send();
		response = httpClient.newRequest("http://domain.com/upload").content(new PathContentProvider(Paths.get("file_to_upload.txt")), "text/plain").send();
		response = httpClient.newRequest("http://domain.com/upload").content(new InputStreamContentProvider(new FileInputStream("file_to_upload.txt")), "text/plain").send();

		byte[] bytes = new byte[10];
		response = httpClient.newRequest("http://domain.com/upload").content(new BytesContentProvider(bytes), "text/plain").send();

		// impose a total timeout for the request/response conversation
		response = httpClient.newRequest("http://domain.com/path?query").timeout(5, TimeUnit.SECONDS).send();

		// 异步访问方式（非阻塞）
		httpClient.newRequest("http://domain.com/path").timeout(3, TimeUnit.SECONDS).send(new Response.CompleteListener()
		{
			@Override
			public void onComplete(final Result result)
			{
				// Your logic here
			}
		});

		// If the request content is not immediately available, but your application will be notified of the content to send, you can use
		// DeferredContentProvider in this way:
		DeferredContentProvider content = new DeferredContentProvider();
		httpClient.newRequest("http://domain.com/upload").content(content).send(new Response.CompleteListener()
		{
			@Override
			public void onComplete(final Result result)
			{
				// Your logic here
			}
		});

		// Content not available yet here

		// ...
		Thread.sleep(5000);
		// An event happens, now content is available
		bytes = new byte[20];
		content.offer(ByteBuffer.wrap(bytes));

		// ...
		Thread.sleep(5000);
		// All content has arrived
		content.close();

		OutputStreamContentProvider streamContent = new OutputStreamContentProvider();

		// Use try-with-resources to close the OutputStream when all content is written
		try (OutputStream output = streamContent.getOutputStream())
		{
			httpClient.newRequest("localhost", 8080).content(streamContent).send(new Response.CompleteListener()
			{
				@Override
				public void onComplete(final Result result)
				{
					// Your logic here
				}
			});

			// Write content
			// writeContent(output);
		}
		// End of try-with-resource, output.close() called automatically to signal end of content

		// when using the blocking APIs (see Blocking APIs) and the content is buffered in memory within a ContentResponse up to 2 MiB

		// you can use a org.eclipse.jetty.client.util.FutureResponseListener to control the length of the response content (for example limiting to values
		// smaller than the default of 2 MiB)
		Request request = httpClient.newRequest("http://domain.com/path");
		// Limit response content buffer to 512 KiB
		FutureResponseListener listener = new FutureResponseListener(request, 512 * 1024);
		request.send(listener);
		// If the response content length is exceeded, the response will be aborted, and an exception will be thrown by method get().
		response = listener.get(5, TimeUnit.SECONDS);

		// If you are using the asynchronous APIs (see Asynchronous APIs), you can use the BufferingResponseListener utility class:
		httpClient.newRequest("http://domain.com/path")
		// Buffer response content up to 8 MiB
				.send(new BufferingResponseListener(8 * 1024 * 1024)
				{
					@Override
					public void onComplete(final Result result)
					{
						if (!result.isFailed())
						{
							byte[] responseContent = getContent();
							// Your logic here
						}
					}
				});

		// The second way is the most efficient (because it avoids content copies) and allows you to specify a Response.ContentListener, or a subclass, to
		// handle the content as soon as it arrives:
		// Todo:

		// The third way allows you to wait for the response and then stream the content using the InputStreamResponseListener utility class:
		InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener();
		httpClient.newRequest("http://domain.com/path").send(inputStreamResponseListener);
		// Wait for the response headers to arrive
		Response response1 = inputStreamResponseListener.get(5, TimeUnit.SECONDS);
		// Look at the response
		if (response1.getStatus() == 200)
		{
			InputStream responseContent = inputStreamResponseListener.getInputStream();
			// Your logic here
		}
	}
}
